//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-03-15     ZhangYihua   first version
//
// Description  : 
//################################################################################

// cell fifo with tpram 
module tpram_cfifo #(
parameter           CDATA_BW                = 18,       // cell data bit width
parameter           CLEN_MAX                = 64,       // the longest cell length <= CLEN_MAX < MEM_DEPTH - WRAP_NUM_ADAE*2
parameter           MEM_DEPTH               = 512,      // it must be even if ASYNC_CLK==1'b1, actural maximum wr_used is MEM_DEPTH-1
parameter           WRAP_NUM_ADAE           = 0,        // the number of added data after EOC which will be wrapped to head of cell
                                                        // 0:no added data after EOC, all data is written to mem sequentilly;
                                                        // x(x>=1):x added data after EOC is promoted ahead of original head of cell;
parameter           ASYNC_CLK               = 1'b0,     // clock of read and write is 1'b0:synchronous; 1'b1:asynchronous
parameter           SYNC_NUM_W2R            = 2,
parameter           SYNC_NUM_R2W            = 2,
parameter           GRAY_ADDR_LPL           = 0,        // if RAPL=WAPL=0, let GRAY_ADDR_LPL=0, else 1
parameter           RDPL_NUM                = 2,        // read operation pipeline steps, include read address and data pipeline
// rapl:    read address pipeline means pipeline steps from mem_raddr to SRAM, 0 means no DFF between them
// wapl:    write address pipeline means pipeline steps from mem_waddr to SRAM, 0 means no DFF between them
// it's required that rapl must equals to wapl at any case.
// case 1: rapl=wapl=0, normal operation is ok;
// case 2: rapl=wapl=1 with prefetch mode 'RDPL_CACHE', normal operation is ok;
// case 3: rapl=wapl=1 with prefetch mode 'RDPL_SHIFT' or 'RDPL_STUFF', let DEPTH = theory_estimation + 1, and
//                     take afull as back pressure to avoid SRAM to overflow;
// case others: not recommended;
parameter           ARCH_MODE               = "RDPL_SHIFT", // the smallest area
//parameter           ARCH_MODE               = "RDPL_STUFF", // no bubble exist, but memory must support individul CE for each RDPL step
//parameter           ARCH_MODE               = "RDPL_CACHE", // no bubble exist, but more cache REGs comsumed, about (RDPL_NUM+2)*DATA_BW
parameter           ECC_MODE                = "NONE",       // no ECC
//parameter           ECC_MODE                = "SEC_ONLY",   // Single Error Correction only, not support Double Error Detection

// the following parameters are calculated automatically
parameter           WRAP_CNT_BW             = (WRAP_NUM_ADAE<=2) ? 1 : $clog2(WRAP_NUM_ADAE),
parameter           ADDR_BW                 = $clog2(MEM_DEPTH)     // address range [0:MEM_DEPTH-1]
) ( 
input                                       rst_wr_n,
input                                       clk_wr,

output                                      wr_rdy,     // 1:local side is ready for taking cell data; 0:full
input                                       wr_vld,     // 1:peer side is valid for offerring cell data; 0:empty
input               [CDATA_BW-1:0]          wr_data,    // both wr_vld and wr_rdy asserting means one successful transfering cell data
input                                       wr_eoc,     // end of cell, valid when wr_vld==1'b1, don't care when others
input                                       wr_drop,    // high pulse anywhere regardless of wr_vld means to drop current cell

// unused if WRAP_NUM_ADAE=0
output                                      wrap_rdy,
output              [WRAP_CNT_BW-1:0]       wrap_cnt,   // increase from 0 to WARP_NUM_ADAE-1
output                                      wrap_end,
input               [CDATA_BW-1:0]          wrap_data,
input               [WRAP_CNT_BW-1:0]       wrap_ofst,  // offset address for wrap_data, within [0:WRAP_NUM_ADAE-1]
input                                       wrap_drop,  // high pulse after wr_eoc but before wrap_end(included) means to drop current cell

input               [ADDR_BW-1:0]           cfg_afull_th,
output              [ADDR_BW-1:0]           wr_cnt,     // 0 <= wr_cnt <= MEM_DEPTH-1
output                                      wr_afull,   // wr_afull assert when wr_cnt>cfg_afull_th
output                                      wr_err,     // cell data is over length and droped

input                                       rst_rd_n,
input                                       clk_rd,

input                                       rd_rdy,     // 1:peer side is ready for taking cell data; 0:busy
output                                      rd_vld,     // 1:local side is valid for offerring cell data(at least a completed cell); 0:empty
output              [CDATA_BW-1:0]          rd_data,    // both rd_vld and rd_rdy asserting means one successful transfering  cell data
output                                      rd_eoc 
);

// basic waveform
// (1) cell write side (WRAP_NUM_ADAE=3, n<=CLEN_MAX, 0<=x,y,z<3)
//                               |<-------------------- same cell ----------------------------->|
//                               |<--------------- original data ------------>|<----- WRAP ---->|
//                     _____________________        __________________________                   ______________________
// wr_rdy(o)   :______|                       ~~~~                            |_________________|
//                                                                                                                            
//                                __________        __________________________                          
// wr_vld(i)   :_________________|            ~~~~                            |________________________________________
//              _________________ __________        ___________________________________________________________________
// wr_data(i)  :                 |d1 |d2 |    ~~~~    |   |   |   |   |   |dn |                                         
//              ----------------------------        -------------------------------------------------------------------
//                                                                         ___                                   
// wr_eoc(i)   :____________________________  ~~~~  ______________________|   |________________________________________
//                                                                             _________________
// wrap_rdy(o) :____________________________  ~~~~  __________________________|                 |______________________
//                                                                                         _____                                   
// wrap_end(i) :____________________________  ~~~~  ______________________________________|     |______________________
//              ____________________________        ____________________________________________ ______________________
// wrap_cnt(o) :                              ~~~~                            | 0   | 1   | 2   |                       
//              ----------------------------        -------------------------------------------------------------------
//              ____________________________        ____________________________________________ ______________________
// wrap_data(i):                              ~~~~                            | adx | ady | adz |                       
//              ----------------------------        -------------------------------------------------------------------
//              ____________________________        ____________________________________________ ______________________
// wrap_ofst(i):                              ~~~~                            | x   | y   | z   |                       
//              ----------------------------        -------------------------------------------------------------------
//              _________________ __________        ____________________________________________ ______________________
// mem_waddr   :                 |A+1|A+2|    ~~~~    |   |   |   |   |   |A+n|A-2+x|A-2+y|A-2+z|                       
//              ----------------------------        -------------------------------------------------------------------
//              _________________ __________        ____________________________________________ ______________________
// wr_addr     :                 |A+1|A+2|    ~~~~    |   |   |   |   |   |A+n|A+n+1|A+n+2|A+n+3|                       
//              ----------------------------        -------------------------------------------------------------------
//              _________________ ______________________________________________________________ ______________________
// soc_addr    :                 |           A-2                                                |        A+n+1          
//              -------------------------------------------------------------------------------------------------------

//################################################################################
// define local varialbe and localparam
//################################################################################

wire                                        mem_wen;
wire                [ADDR_BW-1:0]           mem_waddr;
wire                [CDATA_BW+1-1:0]        mem_wdata;
wire                                        mem_ren;
wire                [RDPL_NUM-1:0]          mem_rdpl_ce;
wire                [ADDR_BW-1:0]           mem_raddr;
wire                [CDATA_BW+1-1:0]        mem_rdata;

//################################################################################
// main
//################################################################################

// cell fifo controller
cfifo_ctrl #(
        .CDATA_BW                       (CDATA_BW                       ),
        .CLEN_MAX                       (CLEN_MAX                       ),
        .MEM_DEPTH                      (MEM_DEPTH                      ),
        .WRAP_NUM_ADAE                  (WRAP_NUM_ADAE                  ),
        .ASYNC_CLK                      (ASYNC_CLK                      ),
        .SYNC_NUM_W2R                   (SYNC_NUM_W2R                   ),
        .SYNC_NUM_R2W                   (SYNC_NUM_R2W                   ),
        .GRAY_ADDR_LPL                  (GRAY_ADDR_LPL                  ),
        .RDPL_NUM                       (RDPL_NUM                       ),
        .ARCH_MODE                      (ARCH_MODE                      )
) u_cfifo_ctrl ( 
        .rst_wr_n                       (rst_wr_n                       ),
        .clk_wr                         (clk_wr                         ),

        .wr_rdy                         (wr_rdy                         ),
        .wr_vld                         (wr_vld                         ),
        .wr_data                        (wr_data                        ),
        .wr_eoc                         (wr_eoc                         ),
        .wr_drop                        (wr_drop                        ),

// unused if WRAP_NUM_ADAE=0
        .wrap_rdy                       (wrap_rdy                       ),
        .wrap_cnt                       (wrap_cnt                       ),
        .wrap_end                       (wrap_end                       ),
        .wrap_data                      (wrap_data                      ),
        .wrap_ofst                      (wrap_ofst                      ),
        .wrap_drop                      (wrap_drop                      ),

        .cfg_afull_th                   (cfg_afull_th                   ),
        .wr_cnt                         (wr_cnt                         ),
        .wr_afull                       (wr_afull                       ),
        .wr_err                         (wr_err                         ),

        .rst_rd_n                       (rst_rd_n                       ),
        .clk_rd                         (clk_rd                         ),

        .rd_rdy                         (rd_rdy                         ),
        .rd_vld                         (rd_vld                         ),
        .rd_data                        (rd_data                        ),
        .rd_eoc                         (rd_eoc                         ),

        .mem_wen                        (mem_wen                        ),
        .mem_waddr                      (mem_waddr                      ),
        .mem_wdata                      (mem_wdata                      ),

        .mem_ren                        (mem_ren                        ),
        .mem_rdpl_ce                    (mem_rdpl_ce                    ),
        .mem_raddr                      (mem_raddr                      ),
        .mem_rdata                      (mem_rdata                      )
);

// two-port RAM
tpram_wrap #(
        .DEPTH                          (MEM_DEPTH                      ),
        .DATA_BW                        (CDATA_BW+1                     ),
        .RDPL_NUM                       (RDPL_NUM                       ),
        .USER_DEF_TAG                   ("NONE"                         ),
        .ECC_MODE                       (ECC_MODE                       )
) u_tpram ( 
        .rst_wr_n                       (rst_wr_n                       ),
        .clk_wr                         (clk_wr                         ),
        .mem_wen                        (mem_wen                        ),
        .mem_waddr                      (mem_waddr                      ),
        .mem_wdata                      (mem_wdata                      ),

        .cfg_frc_sbe                    (1'b0                           ),

        .rst_rd_n                       (rst_rd_n                       ),
        .clk_rd                         (clk_rd                         ),
        .mem_ren                        (mem_ren                        ),
        .mem_rdpl_ce                    (mem_rdpl_ce                    ),
        .mem_raddr                      (mem_raddr                      ),
        .mem_rvld                       (                               ),
        .mem_rdata                      (mem_rdata                      ),

        .alm_ecc_err                    (                               ),
        .alm_ecc_dbe                    (                               )
);

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
